/// <reference path="../libs/Cesium/Cesium.js" />

let DrawLineControl = (function () {

    function DrawLineControl(viewer) {
        this.viewer = viewer;

        //变量
        this.hander = undefined;
        this.id = 0;
        this.lastPos = undefined;
        this.move = false;
        this.selectedPoint = undefined;
        this.points = [];
        this.selectedIndex = -1;
        this.lineEntities = [];
        this.pointEntities = [];
        this.addPoint = true;
        this.callback = undefined;
    }

    DrawLineControl.prototype.startDraw = function () {
        this.clear();

        this.hander = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

        this.hander.setInputAction(e => {
            if (this.move) return;
            if (!this.addPoint) { this.addPoint = true; return; }
            let cartesian = this.viewer.camera.pickEllipsoid(e.position, this.viewer.scene.globe.ellipsoid);
            if (!cartesian) return;
            let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
            let lng = Cesium.Math.toDegrees(cartographic.longitude);
            let lat = Cesium.Math.toDegrees(cartographic.latitude);
            let point = new Cesium.Cartesian3.fromDegrees(lng, lat);
            this.points.push(point);
            this.showLine();
            if (this.callback) this.callback(this.points);
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

        this.hander.setInputAction(e => {
            let picked = undefined;
            this.viewer.scene.drillPick(e.position).map(item => {
                if (item.id.polyline) picked = item.id;
            });
            this.viewer.scene.drillPick(e.position).map(item => {
                if (item.id.point) picked = item.id;
            });
            if (!picked) return;
            this.selectedPoint = picked;
            if (this.selectedPoint instanceof Cesium.Entity) {
                if (this.selectedPoint.point) {
                    this.viewer.scene.screenSpaceCameraController.enableRotate = false;//锁定相机
                    this.selectedPoint.point.outlineColor = Cesium.Color.fromCssColorString("#ffff00").withAlpha(0.8);
                    this.move = true;

                    for (let i = 0; i < this.pointEntities.length; i++) {
                        if (this.pointEntities[i].id == this.selectedPoint.id) {
                            this.selectedIndex = i;
                        }
                    }
                } else {
                    let cartesian = this.viewer.camera.pickEllipsoid(e.position, this.viewer.scene.globe.ellipsoid);
                    if (!cartesian) return;
                    this.selectedIndex = this.getIndex(cartesian);
                    this.addPoint = false;

                    let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
                    let lng = Cesium.Math.toDegrees(cartographic.longitude);
                    let lat = Cesium.Math.toDegrees(cartographic.latitude);
                    let pnt = new Cesium.Cartesian3.fromDegrees(lng, lat);
                    this.points.splice(this.selectedIndex + 1, 0, pnt);

                    this.id = this.id + 1;
                    let lineId = "line" + this.id;
                    let line = {
                        id: lineId,
                        polyline: {
                            positions: [this.points[this.selectedIndex], this.points[this.selectedIndex + 1]],
                            width: 8.0,
                            clampToGround: false, //clampToGround属性设置线是否贴地，默认为flase, 不贴地
                            material: Cesium.Color.fromCssColorString("#ff0000").withAlpha(0.8),
                            depthFailMaterial: Cesium.Color.fromCssColorString("#ff0000").withAlpha(0.8)
                        }
                    };
                    this.viewer.entities.add(line);
                    this.lineEntities.splice(this.selectedIndex, 0, this.viewer.entities.getById(lineId));

                    let pointId = "point" + this.id;
                    let point = {
                        id: pointId,
                        position: pnt,
                        point: {
                            pixelSize: 20,
                            color: Cesium.Color.fromCssColorString("#ffffff").withAlpha(0.8),
                            outlineColor: Cesium.Color.fromCssColorString("#0000ff").withAlpha(0.8),
                            outlineWidth: 3,
                            disableDepthTestDistance: Number.POSITIVE_INFINITY
                        }
                    };
                    this.viewer.entities.add(point);
                    this.pointEntities.splice(this.selectedIndex + 1, 0, this.viewer.entities.getById(pointId));

                    this.lineEntities[this.selectedIndex + 1].polyline.positions = [this.points[this.selectedIndex + 1], this.points[this.selectedIndex + 2]];

                    this.viewer.entities.computeAvailability();
                    if (this.callback) this.callback(this.points);
                }
            }
        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

        this.hander.setInputAction(async e => {
            if (!this.move) return;
            while (await this.check() < 100) { }
            this.viewer.scene.screenSpaceCameraController.enableRotate = true;//取消锁定相机
            this.move = false;
            this.lastPos = undefined;
            if (this.selectedPoint) this.selectedPoint.point.outlineColor = Cesium.Color.fromCssColorString("#0000ff").withAlpha(0.8);

            let val = this.selectedPoint.position.getValue();
            this.points[this.selectedIndex] = val;
            if (this.selectedIndex - 1 >= 0) {
                this.lineEntities[this.selectedIndex - 1].polyline.positions = [this.points[this.selectedIndex - 1], this.points[this.selectedIndex]];
            }
            if (this.selectedIndex + 1 < this.points.length) {
                this.lineEntities[this.selectedIndex].polyline.positions = [this.points[this.selectedIndex], this.points[this.selectedIndex + 1]];
            }
            this.viewer.entities.computeAvailability();
            if (this.callback) this.callback(this.points);
        }, Cesium.ScreenSpaceEventType.LEFT_UP);

        this.hander.setInputAction(e => {
            if (this.move && this.selectedPoint && this.selectedPoint.point) {
                let cartesian = this.viewer.camera.pickEllipsoid(e.endPosition, this.viewer.scene.globe.ellipsoid);
                let pos = this.getPos(cartesian);

                if (!this.lastPos) {
                    this.lastPos = pos;
                    return;
                }

                let lng = pos[0] - this.lastPos[0];
                let lat = pos[1] - this.lastPos[1];

                let val = this.selectedPoint.position.getValue();
                let old = this.getPos(val);

                this.selectedPoint.position = new Cesium.Cartesian3.fromDegrees(old[0] + lng, old[1] + lat, 0);

                this.lastPos = pos;
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

        this.hander.setInputAction(e => {
            let picked = undefined;
            this.viewer.scene.drillPick(e.position).map(item => {
                if (item.id.point) picked = item.id;
            });
            if (!picked) return;
            this.selectedPoint = picked;
            if (this.selectedPoint instanceof Cesium.Entity) {
                let val = this.selectedPoint.position.getValue();
                for (let i = 0; i < this.pointEntities.length; i++) {
                    if (this.pointEntities[i].id == this.selectedPoint.id) {
                        if (i < this.lineEntities.length) {
                            let line = this.lineEntities[i];
                            this.viewer.entities.remove(line);
                        } else {
                            let line = this.lineEntities[this.lineEntities.length - 1];
                            this.viewer.entities.remove(line);
                        }
                        let point = this.pointEntities[i];
                        this.viewer.entities.remove(point);

                        this.points.splice(i, 1);
                        this.pointEntities.splice(i, 1);

                        if (i - 1 >= 0) {
                            if (i < this.lineEntities.length) {
                                this.lineEntities[i - 1].polyline.positions = [this.points[i - 1], this.points[i]];
                            }
                        }

                        if (i < this.lineEntities.length) {
                            this.lineEntities.splice(i, 1);
                        } else {
                            this.lineEntities.splice(this.lineEntities.length - 1, 1);
                        }
                        this.viewer.entities.computeAvailability();
                        if (this.callback) this.callback(this.points);
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    }

    DrawLineControl.prototype.check = function () {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (!this.checkCount) this.checkCount = 0;
                this.checkCount++;
                if (this.selectedPoint) {
                    resolve(999);
                } else {
                    resolve(this.checkCount);
                }
            }, 100);
        });
    }

    DrawLineControl.prototype.endDraw = function () {
        if (this.hander && !this.hander.isDestroyed()) {
            this.hander.destroy();
            this.hander = undefined;
        }
    };

    DrawLineControl.prototype.setCallback = function (callback) {
        this.callback = callback;
    }

    DrawLineControl.prototype.clear = function () {
        this.lineEntities.map(item => {
            this.viewer.entities.remove(item);
        });
        this.pointEntities.map(item => {
            this.viewer.entities.remove(item);
        });
        this.points = [];
        this.pointEntities = [];
        this.lineEntities = [];
    };

    DrawLineControl.prototype.showLine = function () {
        this.id = this.id + 1;

        if (this.points.length > 1) {
            let lineId = "line" + this.id;
            let line = {
                id: lineId,
                polyline: {
                    positions: [this.points[this.points.length - 2], this.points[this.points.length - 1]],
                    width: 8.0,
                    clampToGround: false, //clampToGround属性设置线是否贴地，默认为flase, 不贴地
                    material: Cesium.Color.fromCssColorString("#ff0000").withAlpha(0.8),
                    depthFailMaterial: Cesium.Color.fromCssColorString("#ff0000").withAlpha(0.8)
                }
            };
            this.viewer.entities.add(line);
            this.lineEntities.push(this.viewer.entities.getById(lineId));
        }

        let pointId = "point" + this.id;
        let point = {
            id: pointId,
            position: this.points[this.points.length - 1],
            point: {
                pixelSize: 20,
                color: Cesium.Color.fromCssColorString("#ffffff").withAlpha(0.8),
                outlineColor: Cesium.Color.fromCssColorString("#0000ff").withAlpha(0.8),
                outlineWidth: 3,
                disableDepthTestDistance: Number.POSITIVE_INFINITY
            }
        };
        this.viewer.entities.add(point);
        this.pointEntities.push(this.viewer.entities.getById(pointId));
    }

    DrawLineControl.prototype.getPos = function (cartesian) {
        let cartographic = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
        let lng = Cesium.Math.toDegrees(cartographic.longitude);
        let lat = Cesium.Math.toDegrees(cartographic.latitude);
        return [lng, lat];
    }

    DrawLineControl.prototype.getIndex = function (cartesian) {
        let minDistance;
        let index = -1;

        let p = this.getPos(cartesian);

        for (let i = 0; i < this.points.length - 1; i++) {
            let p1 = this.getPos(this.points[i]);
            let p2 = this.getPos(this.points[i + 1]);

            let point = turf.point(p);
            let line = turf.lineString([p1, p2]);

            let distance = turf.pointToLineDistance(point, line, { units: 'kilometers' });
            if (!minDistance) {
                minDistance = distance;
                index = i;
            } else {
                if (distance < minDistance) {
                    minDistance = distance;
                    index = i;
                }
            }
        }

        return index;
    };

    return DrawLineControl;
})();
